package com.mh.rtmppublisher;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.hardware.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class MainActivity extends Activity{

    long startTime =0;
    private int previewTime = 0;
    private final static String CLASS_LABEL = "Main";
    private final static String LOG_TAG = CLASS_LABEL;
    private String rtmpUrl = "rtmp://192.168.3.11:1935/live/android";
    private Camera cameraDevice;
    private CameraView cameraView;
    private int imageWidth=640;
    private int imageHeight=480;
    private final int frameRate =5;
    private Frame yuvImage = null;
    private FFmpegFrameRecorder recorder;
    private EditText urlInput;
    private Button btnRecorderControl;
    private boolean isPreviewOn = false;
    private boolean recording =false;
    private TextView status;
    private SharedPreferences sp;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //锁定横屏
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        //禁止熄屏
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        init();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        recording = false;
        if (cameraView != null) {
            cameraView.stopPreview();
        }
        if (cameraDevice != null) {
            cameraDevice.stopPreview();
            cameraDevice.release();
            cameraDevice = null;
        }
    }

    //初始化工作
    private void init() {

        //获取链接数据
        sp = getSharedPreferences("Publisher", MODE_PRIVATE);
        rtmpUrl = sp.getString("rtmpUrl", rtmpUrl);

        //获取屏幕参数
        Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        int screenWidth = display.getWidth();
        int screenHeight = display.getHeight();

        LayoutInflater myInflate = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        RelativeLayout topLayout = new RelativeLayout(this);
        setContentView(topLayout);

        RelativeLayout preViewLayout = (RelativeLayout) myInflate.inflate(R.layout.activity_main, null);
        RelativeLayout.LayoutParams layoutParam = new RelativeLayout.LayoutParams(screenWidth, screenHeight);
        topLayout.addView(preViewLayout, layoutParam);

        /* 链接与按钮控件 */
        status = (TextView) findViewById(R.id.status_indicator);
        status.setVisibility(View.INVISIBLE);
        urlInput = (EditText) findViewById(R.id.urlInput);
        urlInput.setText(rtmpUrl);
        btnRecorderControl = (Button) findViewById(R.id.control);
        btnRecorderControl.setText("开始");
        btnRecorderControl.setOnClickListener(v -> {
            if (!recording) {
                rtmpUrl = urlInput.getText().toString();
                SharedPreferences.Editor editor = sp.edit();
                editor.putString("rtmpUrl", rtmpUrl);
                editor.apply();
                startPublishing();
                btnRecorderControl.setText("停止");
            } else {
                stopPublishing();
                btnRecorderControl.setText("开始");
            }
        });

        /* 相机画面预览*/
        int prev_rw = (int) (3.0* screenWidth /4);
        int prev_rh = (int) (3.0* screenHeight /4);
        layoutParam = new RelativeLayout.LayoutParams(prev_rw, prev_rh);
        layoutParam.topMargin = (int) (screenHeight /8);
        layoutParam.leftMargin = (int) (screenWidth /8);
        try {
            cameraDevice = Camera.open();
        } catch (Exception e){
            Log.e(LOG_TAG, "camera open failed");
            finish();
            return;
        }
        Log.i(LOG_TAG, "camera open successfully");
        cameraView = new CameraView(this, cameraDevice);
        topLayout.addView(cameraView, layoutParam);
        Log.i(LOG_TAG, "camera preview : OK");
    }

    //返回键处理
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
                finish();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
    private void initRecorder() {
        Log.w(LOG_TAG, "init recorder");
        if (yuvImage == null) {
            yuvImage = new Frame(imageWidth, imageHeight, Frame.DEPTH_UBYTE, 2);
            Log.i(LOG_TAG, "create yuvImage");
        }
        Log.i(LOG_TAG, "ffmpeg_url: " + rtmpUrl);
        recorder = new FFmpegFrameRecorder(rtmpUrl, imageWidth, imageHeight, 0);
        recorder.setFormat("flv");
        recorder.setFrameRate(frameRate);
        Log.i(LOG_TAG, "recorder initialize success");
    }
    private void startPublishing() {
        initRecorder();
        try {
            recorder.start();
            startTime = System.currentTimeMillis();
            recording = true;
            status.setVisibility(View.VISIBLE);
        } catch (FFmpegFrameRecorder.Exception e) {
            e.printStackTrace();
        }
    }
    public void stopPublishing() {
        if (recorder != null && recording) {
            recording = false;
            status.setVisibility(View.INVISIBLE);
            Log.v(LOG_TAG, "Finished Publishing");
            try {
                recorder.stop();
                recorder.release();
                status.setVisibility(View.INVISIBLE);
            } catch (FFmpegFrameRecorder.Exception e) {
                e.printStackTrace();
            }
            recorder = null;
        }
    }
    class CameraView extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback {

        private SurfaceHolder mHolder;
        private Camera mCamera;

        public CameraView(Context context, Camera camera) {
            super(context);
            Log.w("camera", "camera view");
            mCamera = camera;
            mHolder = getHolder();
            mHolder.addCallback(CameraView.this);
            mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
            mCamera.setPreviewCallback(CameraView.this);
//            mCamera.setDisplayOrientation(90);
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            try {
                stopPreview();
                mCamera.setPreviewDisplay(holder);
            } catch (IOException exception) {
                mCamera.release();
                mCamera = null;
            }
        }

        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            stopPreview();

            Camera.Parameters camParams = mCamera.getParameters();
            List<Camera.Size> sizes = camParams.getSupportedPreviewSizes();
            // Sort the list in ascending order
            Collections.sort(sizes, new Comparator<Camera.Size>() {

                public int compare(final Camera.Size a, final Camera.Size b) {
                    return a.width * a.height - b.width * b.height;
                }
            });

            // Pick the first preview size that is equal or bigger, or pick the last (biggest) option if we cannot
            // reach the initial settings of imageWidth/imageHeight.
            for (int i = 0; i < sizes.size(); i++) {
                if ((sizes.get(i).width >= imageWidth && sizes.get(i).height >= imageHeight) || i == sizes.size() - 1) {
                    imageWidth = sizes.get(i).width;
                    imageHeight = sizes.get(i).height;
                    Log.v(LOG_TAG, "Changed to supported resolution: " + imageWidth + "x" + imageHeight);
                    break;
                }
            }
            camParams.setPreviewSize(imageWidth, imageHeight);

            Log.v(LOG_TAG, "Setting imageWidth: " + imageWidth + " imageHeight: " + imageHeight + " frameRate: " + frameRate);

//            camParams.setPreviewFrameRate(frameRate);
            Log.v(LOG_TAG, "Preview Framerate: " + camParams.getPreviewFrameRate());

            mCamera.setParameters(camParams);

            // Set the holder (which might have changed) again
            try {
                mCamera.setPreviewDisplay(holder);
                mCamera.setPreviewCallback(CameraView.this);
                startPreview();
            } catch (Exception e) {
                Log.e(LOG_TAG, "Could not set preview display in surfaceChanged");
            }
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            try {
                mHolder.addCallback(null);
                mCamera.setPreviewCallback(null);
            } catch (RuntimeException e) {
                // The camera has probably just been released, ignore.
            }
        }

        public void startPreview() {
            if (!isPreviewOn && mCamera != null) {
                isPreviewOn = true;
                mCamera.startPreview();
            }
        }

        public void stopPreview() {
            if (isPreviewOn && mCamera != null) {
                isPreviewOn = false;
                mCamera.stopPreview();
            }
        }

        @Override
        public void onPreviewFrame(byte[] data, Camera camera) {
            /* get video data */
            if (yuvImage != null && recording && previewTime ++ % 6 == 0) {
                ((ByteBuffer) yuvImage.image[0].position(0)).put(data);

                try {
                    Log.v(LOG_TAG, "Writing Frame");
                    long t = 1000 * (System.currentTimeMillis() - startTime);
                    if (t > recorder.getTimestamp()) {
                        recorder.setTimestamp(t);
                    }
                    recorder.record(yuvImage);
                } catch (FFmpegFrameRecorder.Exception e) {
                    Log.v(LOG_TAG, e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    }
}